﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using AutoShuntingSchedule.DAL;
using FileLibrary.Models;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;

namespace FileLibrary
{
    /// <summary>
    /// 配置文件读取
    /// 去掉业务，保持数据层稳定
    /// 
    /// </summary>
    public class FileManage
    {
        #region Field

        private const string FileName = "调车计划自动编制.xls";
        private const string FileFolder = "JXPM_CONFIG";

        /// <summary>
        /// 序列化对象
        /// </summary>
        private readonly List<SerializableModel> SerializableModels = new List<SerializableModel>();
        /// <summary>
        /// 业务对象类，Excel文件表名对应Models下的类Description
        /// </summary>
        public readonly Dictionary<string, Type> ClassDictionary = new Dictionary<string, Type>();

        #endregion

        #region Property

        public static FileManage Instance = new Lazy<FileManage>(() => new FileManage()).Value;
        public Dictionary<string, FileModel> FileModes { get; } = new Dictionary<string, FileModel>();
        //配置文件的工作表名列表
        public List<string> SheetNameList { get; set; } = new List<string>();

        #endregion

        private FileManage()
        {
            LoadSerializableModel();
            PackModels();
        }

        /// <summary>
        /// 组装业务模型
        /// 通过反射技术赋值
        /// 核心设计，解决了数据结构变化带来的问题，增加业务类型的配置数据不影响
        /// </summary>
        private void PackModels()
        {
            InitClassDictionary();
            foreach (var model in SerializableModels)
            {
                var fileModel = GetFileModel(model);
                SetValueForFileModel(fileModel, model);
            }
        }

        /// <summary>
        /// 通过反射为业务对象的属性自动赋值，Excel表格的列和属性Description对应
        /// 支持增加删除业务对象属性，属性的可拓展性
        /// </summary>
        /// <param name="fileModel"></param>
        /// <param name="model"></param>
        private static void SetValueForFileModel(FileModel fileModel, SerializableModel model)
        {
            if (fileModel == null) return;
            PropertyInfo[] fields = fileModel.GetType().GetProperties(); //获取指定对象的所有公共属性
            foreach (var propertyInfo in fields)
            {
                foreach (var attribute in propertyInfo.GetCustomAttributes(false))
                {
                    var des = attribute as DescriptionAttribute;
                    if (des == null) continue;
                    if (des.Description == model.PropertyName)
                    {
                        var en = (CellType)Enum.Parse(typeof(CellType), model.TypeString);

                        try
                        {
                            switch (en)
                            {
                                case CellType.Numeric:
                                    if (model.TypeValue.Contains(":")) //时间
                                    {
                                        propertyInfo.SetValue(fileModel, model.TypeValue);
                                    }
                                    else
                                    {
                                        propertyInfo.SetValue(fileModel, int.Parse(model.TypeValue));
                                    }

                                    break;
                                case CellType.String:
                                    propertyInfo.SetValue(fileModel, model.TypeValue);
                                    break;
                                case CellType.Blank:
                                    break;
                                default:
                                    throw new ArgumentOutOfRangeException();
                            }
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 以ModelKey查询业务对象字典，如果没有，通过反射创建对象。
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private FileModel GetFileModel(SerializableModel model)
        {
            FileModel fileModel;
            if (FileModes.ContainsKey(model.ModelKey))
            {
                //赋值
                fileModel = FileModes[model.ModelKey];
            }
            else
            {
                //反射动态创建数据模型
                var typeStr = model.ModelType;
                if (!ClassDictionary.ContainsKey(typeStr)) return null;//读入的数据，对应Models的对象，没有则不需要
                var type = ClassDictionary[typeStr];
                Assembly asm = Assembly.GetExecutingAssembly();
                Object obj = asm.CreateInstance($"{type.FullName}", false);
                fileModel = (FileModel)obj;
                FileModes.Add(model.ModelKey, fileModel);

            }

            return fileModel;
        }

        /// <summary>
        /// 初始化业务对象字典
        /// 可以随业务对象类的创建自动拓展
        /// 业务对象类放在Models文件夹下即可，增加Description和Excel文件表名对应 
        /// </summary>
        private void InitClassDictionary()
        {
            var types = Assembly.GetExecutingAssembly().ExportedTypes;
            foreach (var type in types)
            {
                var modelsType = type.FullName?.Contains("Models") ?? false;
                if (!modelsType) continue;
                foreach (var attribute in type.GetCustomAttributes(false))
                {
                    var des = attribute as DescriptionAttribute;
                    if (des == null) continue;
                    ClassDictionary.Add(des.Description, type);
                }
            }
        }

        /// <summary>
        /// 通过Excel文件加载序列化对象
        /// 增加中间层，与业务对象解耦
        /// </summary>
        private void LoadSerializableModel()
        {
            try
            {
                var fullPath = $"{Environment.CurrentDirectory}\\{FileFolder}\\{FileName}";
                using (var f = new FileStream(fullPath, FileMode.OpenOrCreate))
                {
                    var book = new HSSFWorkbook(f);
                    for (int i = 0; i < book.NumberOfSheets; i++)
                    {
                        var name = book.GetSheetName(i);
                        AnalyzeSheet(book, name);
                        SheetNameList.Add(name);
                    }
                }
            }
            catch (Exception e)
            {
                Logger.ErrorInfo(e.Message);
            }
        }

        /// <summary>
        /// Excel文件格式
        /// 第一行为标题
        /// 第二行为属性名称，需要与业务对象的Description对应。后续组建业务对象时通过匹配进行反射赋值
        /// 下面每一行为业务对象的值
        /// </summary>
        /// <param name="book"></param>
        /// <param name="sheetName"></param>
        private void AnalyzeSheet(HSSFWorkbook book, string sheetName)
        {
            var idGen = 1;
            var sheet = book.GetSheet(sheetName);
            var row0 = sheet.GetRow(0);//标题
            var row1 = sheet.GetRow(1);//属性行
            var start = 2;
            var propertyNames = row1.Cells.Select(x => x.ToString()).ToList();
            for (int i = start; i <= sheet.LastRowNum; i++)
            {
                var row = sheet.GetRow(i);
                for (int j = 0; j < row.LastCellNum; j++)
                {
                    var model = new SerializableModel()
                    {
                        Id = idGen,
                        Index = j,
                        ModelType = sheetName,
                        PropertyName = propertyNames[j],
                        TypeString = row.Cells[j].CellType.ToString(),
                        TypeValue = row.Cells[j].ToString(),
                    };

                    SerializableModels.Add(model);
                }

                idGen++;
            }
        }
    }
}
